/*
 * $Log: BaseDao.java,v $
 * Revision 1.1  2012/04/13 08:40:06  guor
 * *** empty log message ***
 *
 */
package com.xlcszsgc.core.basedao.base.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;
import javax.persistence.Entity;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import com.xlcszsgc.core.basedao.base.IBaseDao;
import com.xlcszsgc.core.basedao.base.querybean.BaseBean;
import com.xlcszsgc.core.basedao.base.require.BaseRequire;
import com.xlcszsgc.core.basedao.condition.BaseCondition;
import com.xlcszsgc.core.basedao.condition.BasePageCondition;
import com.xlcszsgc.core.log4j.ILog4jManager;

/**
 * Title: BaseDao<br>
 * Description: 持久化层基类<br>
 * Company: ORCHIS<br>
 * Copyright @ 2011 ORCHIS .All rights reserved.<br>
 * @author WangWB
 * @createDate Aug 1, 2011
 * @version $Revision: 1.1 $
 */
@Repository(value = "com.xlcszsgc.core.basedao.base.impl.BaseDao")
public class BaseDao extends HibernateDaoSupport implements IBaseDao {
	// 初始化日志接口
	@Resource(name = "com.xlcszsgc.core.log4j.impl.Log4jManager")
	private ILog4jManager log4jManager;
	// 初始化hql/sql语句拼写类
	@Resource(name = "com.xlcszsgc.core.basedao.base.require.BaseRequire")
	private BaseRequire baseRequire;

	/**
	 * 对sessionFactory的注入，若使用HibernateDaoSupport，则必须写，否则报错
	 * 
	 * @param sessionFactory
	 * @author WangWB
	 * @date Aug 3, 2011
	 */
	@Autowired
	public void setSF(@Qualifier(value = "sessionFactory") SessionFactory sessionFactory) {
		super.setSessionFactory(sessionFactory);
	}

	/**
	 * 获得Session
	 * 
	 * @return
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	public Session getHibernateSession() throws Exception {
		return super.getSession();
	}

	/**
	 * 单个实体入库
	 * 
	 * @param entity
	 *            必须保证entity为带有@Entity标识的对象，并且保证被@Id注解的属性无值
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public void addEntity(Object entity) throws Exception {
		if (entity != null) {
			if (checkAnnotation(entity)) {
				getHibernateTemplate().save(entity);
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(), "addEntity",
					"entity为空");
		}
	}

	/**
	 * 判断entity中是否存在@Entity标识
	 * 
	 * @param entity
	 * @return
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	private boolean checkAnnotation(Object entity) throws Exception {
		boolean result = false;
		Class<?> clazz = entity.getClass();
		// 查看该类上方是否存在Entity注解
		boolean isExtendsEntity = clazz.isAnnotationPresent(Entity.class);
		if (isExtendsEntity) {
			// Method[] method = clazz.getMethods();
			// int sum = 1;
			// for (int i = 0; i < method.length; i++) {
			// // 找到该类中被Id注解了的方法
			// if (method[i].isAnnotationPresent(Id.class)) {
			// // 判断该方法是否无值
			// if (method[i].invoke(clazz, null) == null) {
			// result = true;
			// } else {
			// log4jManager.saveCustomLog(getClass().getName(),
			// "checkAnnotationForAdd", "该类中被Id注解了的方法有值");
			// }
			// break;
			// } else {
			// sum++;
			// }
			// }
			// if (sum == method.length) {
			// log4jManager.saveCustomLog(getClass().getName(),
			// "checkAnnotationForAdd", "该类中没有被Id注解了的方法");
			// }
			result = true;
		} else {
			log4jManager.saveCustomLog(getClass().getName(), "checkAnnotation",
					"该类上方没有定义Entity注解");
		}
		return result;
	}

	/**
	 * 多个同类型实体批量入库
	 * 
	 * @param entities
	 *            实体对象集合，必须保证每个实体对象都带有@Entity标识，并且保证保证被@Id注解的属性无值
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public void batchAddEntities(Collection<?> entities) throws Exception {
		if (entities != null && !entities.isEmpty()) {
			Iterator<?> entityIterator = entities.iterator();
			while (entityIterator.hasNext()) {
				Object entity = entityIterator.next();
				if (checkAnnotation(entity)) {
					getHibernateTemplate().save(entity);
				} else {
					log4jManager.saveCustomLog(getClass().getName(),
							"batchAddEntities", "该类中不存在Entity或Id注解");
				}
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"batchAddEntities", "entities集合为空");
		}
	}

	/**
	 * 根据主键批量删除
	 * 
	 * @param entities
	 *            实体对象集合，必须保证每个实体对象都带有@Entity标识，并且保证被@Id注解的属性有值
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public void batchDeleteEntities(Collection<?> entities) throws Exception {
		if (entities != null && !entities.isEmpty()) {
			Iterator<?> entityIterator = entities.iterator();
			while (entityIterator.hasNext()) {
				Object entity = entityIterator.next();
				if (checkAnnotation(entity)) {
					getHibernateTemplate().delete(entity);
				} else {
					log4jManager.saveCustomLog(getClass().getName(),
							"batchDeleteEntities", "该类中不存在Entity或Id注解");
				}
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"batchDeleteEntities", "entities集合为空");
		}
	}

	/**
	 * 根据主键删除
	 * 
	 * @param entity
	 *            必须保证entity为带有@Entity标识的对象，并且保证被@Id注解的属性有值
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public void deleteEntityByPK(Object entity) throws Exception {
		if (entity != null) {
			if (checkAnnotation(entity)) {
				getHibernateTemplate().delete(entity);
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"deleteEntityByPK", "entity为空");
		}
	}

	/**
	 * 获得HibernateTemplate对象
	 * 
	 * @return
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public HibernateTemplate getSpringHibernateTemplate() throws Exception {
		return getHibernateTemplate();
	}

	/**
	 * 查询数据count
	 * 
	 * @param condition
	 *            查询参数必须继承BasePageCondition
	 * @return
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 3, 2011
	 */
	@Override
	public Long countCollection(BasePageCondition condition) throws Exception {
		Long result = null;
		/**
		 * 从condition中获取Query， 需要自己调用session.createQuery方法，并将占位符等条件拼装好
		 */
		Query query = condition.getQuery();
		if (query != null) {
			result = (Long) query.uniqueResult();
		} else {
			log4jManager.saveCustomLog(getClass().getName(), "countCollection",
					"query为空");
		}
		return result;
	}

	/**
	 * 通过JDBC查询数据count
	 * 
	 * @param condition
	 * @return
	 * @throws Exception
	 * @author LiuHQ
	 * @date 2011-10-24
	 */
	@Override
	public Long countCollectionJDBC(BasePageCondition condition)
			throws Exception {
		Long result = null;

		Statement stmt = condition.getStmt();
		ResultSet rs = null;
		if (stmt != null) {
			if (stmt instanceof PreparedStatement) {
				// 使用占位符
				PreparedStatement pStatement = (PreparedStatement) stmt;
				rs = pStatement.executeQuery();
			} else {
				// 未使用占位符
				if (condition.getSql() != null
						&& !"".equals(condition.getSql())) {
					rs = stmt.executeQuery(condition.getSql());
				} else {
					log4jManager.saveCustomLog(getClass().getName(),
							"countCollectionJDBC", "sql为空");
				}
			}
			while (rs.next()) {
				result = rs.getLong(1);
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"countCollectionJDBC", "stmt为空");
		}
		return result;
	}

	/**
	 * 查询数据集，带翻页
	 * 
	 * @param condition
	 *            查询参数必须继承BasePageCondition
	 * @return
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public Collection<?> findCollectionWithPage(BasePageCondition condition)
			throws Exception {
		Collection<?> result = null;
		// 翻页起始点
		int startRow = condition.getStart();
		// 翻页查询长度
		int rows = condition.getRows();
		/**
		 * 从condition中获取Query， 需要自己调用session.createQuery方法，并将占位符等条件拼装好
		 */
		Query query = condition.getQuery();
		// 设置查询结果封装bean，用于多表连查，封装各表中查出的属性
		if (condition.getBasebean() != null) {
			query.setResultTransformer(Transformers.aliasToBean(condition
					.getBasebean()));
		}
		if (query != null) {
			if (startRow != -1)
				query.setFirstResult(startRow - 1);
			if (rows != -1)
				query.setMaxResults(rows - startRow + 1);
			result = query.list();
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"findCollectionWithPage", "query为空");
		}
		return result;
	}

	/**
	 * 查询JDBC结果集，不带分页，若使用PreparedStatement则无需设置sql
	 * 
	 * @param condition
	 * @return
	 * @throws Exception
	 * @author WangWB
	 * @date Oct 28, 2011
	 */
	@Override
	public Collection<?> findCollectionWithoutPageJDBC(BaseCondition condition)
			throws Exception {
		Collection<?> result = null;
		ResultSet rs = null;
		ResultSetMetaData resultsetmetadata = null;
		Statement stmt = condition.getStmt();
		log4jManager.debugCustomLog(this.getClass().getName(), "findCollectionWithoutPageJDBC", "SQL:"+condition.getSql());
		if (stmt != null) {
			if (stmt instanceof PreparedStatement) {
				// 使用占位符
				PreparedStatement pStatement = (PreparedStatement) stmt;
				rs = pStatement.executeQuery();
			} else {
				// 未使用占位符
				String sql = condition.getSql();
				if (sql != null && !"".equals(sql)) {
					rs = stmt.executeQuery(sql);
				} else {
					log4jManager.saveCustomLog(getClass().getName(),
							"findResultSetWithoutJDBCPage", "sql为空");
				}
			}
			resultsetmetadata = rs.getMetaData();
			// 反射bean
			result = aliasToJDBCBean(resultsetmetadata, rs, condition);
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"findResultSetWithoutJDBCPage", "stmt为空");
		}
		return result;
	}

	/**
	 * 查询JDBC结果集，带分页
	 * 
	 * @param condition
	 * @return
	 * @throws Exception
	 * @author LiuHQ
	 * @date 2011-10-24
	 */
	@Override
	public Collection<?> findCollectionWithPageJDBC(BasePageCondition condition)
			throws Exception {
		Collection<?> result = null;
		ResultSet rs = null;
		ResultSetMetaData resultsetmetadata = null;
		Statement stmt = null;
		try {
			stmt = condition.getStmt();
			// 翻页起始点
			int startRow = condition.getStart();
			// 翻页查询长度
			int rows = condition.getRows();
			int begin = 0;
			int end = 0;
			String sql = condition.getSql();
			if (sql != null && !"".equals(sql)) {
				if (startRow != -1) {
					begin = startRow;
				}
				if (rows != -1) {
					end = rows;
				}
				// 拼接sql
				StringBuffer newSql = baseRequire.findResultSetWithJDBCPageSQL(
						sql, begin, end);
				log4jManager.debugCustomLog(this.getClass().getName(), "findCollectionWithoutPageJDBC", "SQL:"+newSql);
				if (stmt != null) {
					if (stmt instanceof PreparedStatement) {
						// 使用占位符
						PreparedStatement pStatement = (PreparedStatement) stmt;
						rs = pStatement.executeQuery();
					} else {
						// 未使用占位符
						rs = stmt.executeQuery(newSql.toString());
					}
					resultsetmetadata = rs.getMetaData();
					// 反射bean
					result = aliasToJDBCBean(resultsetmetadata, rs, condition);
				} else {
					log4jManager.saveCustomLog(getClass().getName(),
							"findResultSetWithJDBCPage", "stmt为空");
				}
			} else {
				log4jManager.saveCustomLog(getClass().getName(),
						"findResultSetWithJDBCPage", "sql为空");
			}
		} catch (Exception e) {
			e.printStackTrace();
			log4jManager.saveExceptionLog(getClass().getName(),
					"findResultSetWithJDBCPage", "sql为空", e);
			throw new Exception("查询JDBC结果集，带分页异常");
		} finally {
			releaseJDBCConnection(rs, null, null);
		}
		return result;
	}

	/**
	 * 将查询结果封装置BaseBean中，查询语句查询字段需要使用别名，并且别名需和自己的BaseBean中字段名称、类型一致
	 * set方法中set后面全部为大写，例如“setNAME”。浮点型只支持双精度浮点型，若有整形，需要在BaseBean中的各set方法做int转换
	 * 
	 * @param resultsetmetadata
	 * @param condition
	 * @return
	 * @author WangWB
	 * @date Oct 28, 2011
	 */
	@SuppressWarnings("static-access")
	private List<BaseBean> aliasToJDBCBean(ResultSetMetaData resultsetmetadata,
			ResultSet rs, BaseCondition condition) throws Exception {
		List<BaseBean> result = null;
		Class<? extends BaseBean> reflectionClass = condition.getBasebean();
		if (reflectionClass != null) {
			result = new ArrayList<BaseBean>();
			BaseBean baseBean = null;
			int columnIndex = 1;
			// 查询字段个数
			int columnCount = resultsetmetadata.getColumnCount();
			while (rs.next()) {
				// 初始化一个承载对象
				baseBean = reflectionClass.newInstance();
				// 循环每一条数据的字段个数
				for (int rsIndex = 1; rsIndex <= columnCount; rsIndex++) {
					// 每一个字段名称
					String columnName = resultsetmetadata
							.getColumnLabel(rsIndex);
					// 查询字段类型
					String columnType = resultsetmetadata
							.getColumnTypeName(rsIndex);
					// 每一个字段的值
					Object columnValue = rs.getObject(rsIndex);
					// 将数据库字段类型转换成对应的java数据类型
					Class<?> javaTypeClass = DBDataTypeToJAVADataType(columnType);
					// 获得每个字段名对应bean中的set方法名
					String setMethodName = "set" + columnName;
					Method setMethod = null;
					try {
						setMethod = reflectionClass.getMethod(setMethodName,
								javaTypeClass);
					} catch (Exception e) {
						log4jManager.saveCustomLog(getClass().getName(),
								"aliasToJDBCBean", "没有" + setMethodName
										+ "这个方法！");
					}
					// 调用对应的set方法，将这个字段的值封装到对象中
					if (setMethod != null) {
						setMethod.invoke(baseBean,
								DBValueToJAVAValue(columnValue));
					}
				}
				columnIndex++;
				result.add(baseBean);
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(), "aliasToJDBCBean",
					"contion中的BaseBean类为空！");
		}
		return result;
	}

	/**
	 * 数据库中读取出来值的对象类型转换成java数据类型的值
	 * 
	 * @param DBValue
	 * @return
	 * @author WangWB
	 * @date Oct 28, 2011
	 */
	private Object DBValueToJAVAValue(Object dbValue) {
		Object javaValue = null;
		if (dbValue != null) {
			if (dbValue instanceof BigDecimal) {
				BigDecimal tempValue = (BigDecimal) dbValue;
				javaValue = tempValue.doubleValue();
			} else {
				javaValue = dbValue;
			}
		}
		return javaValue;
	}

	/**
	 * 数据库类型转java类型
	 * 
	 * @param DBDataType
	 * @return
	 * @author WangWB
	 * @date Oct 28, 2011
	 */
	private Class<?> DBDataTypeToJAVADataType(String DBDataType) {
		Class<?> javaTypeClass = null;
		if (DBDataType != null && !"".equals(DBDataType)) {
			if (DBDataType.indexOf("NUMBER") != -1) {
				javaTypeClass = Double.class;
			} else if (DBDataType.indexOf("VARCHAR") != -1) {
				javaTypeClass = String.class;
			} else if (DBDataType.indexOf("DATE") != -1) {
				javaTypeClass = Date.class;
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"DBDataTypeToJAVADataType", "DBDataType为空！");
		}
		return javaTypeClass;
	}

	/**
	 * 查询数据集，不带翻页
	 * 
	 * @param condition
	 *            查询参数必须继承BaseCondition
	 * @return
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public Collection<?> findCollectionWithoutPage(BaseCondition condition)
			throws Exception {
		Collection<?> result = null;
		/**
		 * 从condition中获取Query， 需要自己调用session.createQuery方法，并将占位符等条件拼装好
		 */
		Query query = condition.getQuery();
		// 设置查询结果封装bean，用于多表连查，封装各表中查出的属性
		if (condition.getBasebean() != null) {
			query.setResultTransformer(Transformers.aliasToBean(condition
					.getBasebean()));
		}
		if (query != null) {
			result = query.list();
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"findCollectionWithoutPage", "query为空");
		}
		return result;
	}

	/**
	 * 释放由于使用JDBC而开启的ResultSet,Statement,Connection
	 * 
	 * @param rSet
	 *            用于待释放的ResultSet
	 * @param statement
	 *            用于待释放的Statement或PrepareStatement
	 * @param connection
	 *            用于待释放的Connection
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 2, 2011
	 */
	@Override
	public void releaseJDBCConnection(ResultSet rSet, Statement statement,
			Connection connection) throws Exception {
		if (rSet != null) {
			rSet.close();
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"releaseJDBCConnection", "ResultSet为空或已经关闭");
		}
		if (statement != null) {
			statement.close();
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"releaseJDBCConnection", "Statement为空或已经关闭");
		}
		if (connection != null) {
			connection.close();
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"releaseJDBCConnection", "Connection为空或已经关闭");
		}
	}

	/**
	 * 根据主键更新
	 * 
	 * @param entity
	 *            必须保证entity为带有@Entity标识的对象，并且保证被@Id注解的属性有值
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public void updateEntityByPK(Object entity) throws Exception {
		if (entity != null) {
			if (checkAnnotation(entity)) {
				// 更新之前清除session中的托管态的entity类型对象清除掉，防止更新时session中出现重复托管态对象
				getHibernateTemplate().clear();
				getHibernateTemplate().update(entity);
			}
		} else {
			log4jManager.saveCustomLog(getClass().getName(),
					"updateEntityByPK", "entity为空");
		}
	}

	/**
	 * 获得Connection
	 * 
	 * @return
	 * @author WangWB
	 * @date Aug 1, 2011
	 */
	@Override
	public Connection getJDBCConnection() throws Exception {
		return ((SessionImplementor) getSession()).getJDBCContext()
				.borrowConnection();
	}

	/**
	 * 根据主键查询某一对象
	 * 
	 * @param objectClass
	 *            要查询的实体对象
	 * @param id
	 *            主键
	 * @return
	 * @throws Exception
	 * @author WangWB
	 * @date Aug 5, 2011
	 */
	@Override
	public Object findObject(Class<?> objectClass, Object id) throws Exception {
		Object object = objectClass.newInstance();
		if (checkAnnotation(object)) {
			// modify by 水宇飞 2011/12/6
			getHibernateTemplate().clear();// 在修改信息记录到日志时,修改前数据提取时会受缓存影响，所以此处进行清除缓存操作。

			return super.getHibernateTemplate().get(objectClass,
					(Serializable) id);
		}
		return null;
	}

	/**
	 * 查询JDBC结果集，带分页
	 * 
	 * @param condition
	 * @return
	 * @throws Exception
	 * @author LiuHQ
	 * @date 2011-10-24
	 */
	@Override
	@Deprecated
	public ResultSet findResultSetWithJDBCPage(BasePageCondition condition)
			throws Exception {
		ResultSet rs = null;
		Statement stmt = condition.getStmt();
		// 翻页起始点
		int startRow = condition.getStart();
		// 翻页查询长度
		int rows = condition.getRows();
		int begin = 0;
		int end = 0;
		String sql = condition.getSql();
		StringBuffer newSql = new StringBuffer();
		if (sql != null && sql.length() > 0) {
			if (startRow != -1) {
				begin = startRow;
			}
			if (rows != -1) {
				end = rows;
			}
			newSql.append("select r_by_page.* from (").append(sql).append(
					") r_by_page where r_by_page.r_row_num between ").append(
					begin).append(" and ").append(end);
		}
		if (stmt != null) {
			rs = stmt.executeQuery(newSql.toString());
		}

		return rs;
	}
}
